<?php
/**
 * qLoader is important library of qFw. It loads libraries, modules and models - mainly with very useful function LoadFromConfig() and controllers.
 */
class qLoader
{
    /** Array of qModuleInfos. */
    private static $modules = array();
    /** Array of loaded libraries. */
    private static $libraries = array();
    /** Array of loaded models. */
    private static $models = array();
    
    /**
     * Init() initializes main libraries - controllers, logger, abstract.
     */
    public static function Init()
    {
    	require_once lib_path . "loader/moduleinfo.class.php";
	qLoader::LoadLibrary("controllers");
	qLoader::LoadLibrary("logger");
	qLoader::LoadLibrary("abstract");
    }
    
    /**
     * Loads models, modules and libraries specified in $config. $config must have set fields named modules, libraries and models, with elements separated by ', '.
     * @param configuration $config configuration object
     */
    public static function LoadFromConfig($config)
    {
        $modules = explode(', ', $config->modules);
        foreach ($modules as $module)
            self::_load($module, 1);
        	
        $libraries = explode(', ', $config->libraries);
        foreach ($libraries as $library)
            self::_load($library, 0);
        	
        $models = explode(', ', $config->models);
        foreach ($models as $model)
            self::_load($model, 2);
    }
    
    /**
     * Loads module named $name, as child of $parent module.
     * @param string $name name of module
     * @param string $parent name of parent
     * @return boolean 
     */
    public static function LoadModule($name, $parent = null)
    {
        return self::_load($name, 1, $parent);
    }
    
    /**
     * Loads library named $name.
     * @param string $name name of library
     * @return boolean
     */
    public static function LoadLibrary($name)
    {
    	return self::_load($name, 0);    
    }
    
    /**
     * Loads model named $name
     * @param string $name name of model
     * @return boolean
     */
    public static function LoadModel($name)
    {
        return self::_load(str_replace('_', '/', $name), 2);
    }
    
    // $type: 0 = lib; 1 = module; 2 = model
    /**
     * Private function, used by other LoadXXX() functions.
     * @param string $name name of thing to load
     * @param string $type type of thing to load: 0 library, 1 module, 2 model
     * @param string $parent
     * @return boolean 
     */
    private static function _load($name, $type, $parent = null)
    {
        // determine if module/lib/model is already loaded or not
    	if ((self::GetModule(($parent != null ? $parent->Parents() . '/' : '') . $name) != false && $parent == 1) 
    		|| (isset(self::$libraries[$name]) && $type == 0)
    		|| (isset(self::$models[$name])) && $type == 2) 
            return true;
    		
        // create filepath
    	if ($parent == null || ($parent != null && $type == 0))
            $filename = (!$type ? lib_path : ($type == 1 ? modules_path : models_path)) . '/' . $name . '/' . $name . '.' . 
    	   		(!$type ? 'lib' : ($type == 1 ? 'module' : 'model')) . '.php';
        else
            $filename = modules_path . '/' . $parent->Parents() . '/' . $parent->Name() . '/' . $name . '/' . $name . '.module.php';
        
        // file does not exist...
        if (!file_exists($filename))
        {
            // LOG: Module/lib/model not found.
            return false;	    
        }
        
        // include file
        require_once $filename;
        
        // loaded thing is module...
        if ($type == 1)
        {
            // create class name and instance of class
            $class = ($parent != null ? str_replace('/', '_', $parent->Parents().'/'.$parent->Name()) : '') . $name;
	    $module = new $class;
        
            // huh, module does not implement qModule...
    	    if (!($module instanceof qModule))
            {
            	// LOG: Module object is not instance of qModule.
	        return false;
    	    }
    	    
            // register module in qModuleInfo system
    	    if ($parent != null)
                $parent->AddSubmodule($name, $module);
            else
                self::$modules[$name] = new qModuleInfo($name, $module);
        
            // initialize module
            $module->Init();
        }
        
        // library has been loaded, save that
	if ($type == 0)
            self::$libraries[$name] = true;
        
        // model has been loaded, save that
        if ($type == 2)
            self::$models[$name] = true;
        	
        return true;
    }
    
    /**
     * Finds module info of module named $name.
     * @param string $name
     * @return Found module's info / false.
     */
    public static function GetModule($name)
    {
        // found...
    	if (isset(self::$modules[$name]))
            return self::$modules[$name];
    	// not found...
        else
    	{
            list($parent, $name) = explode('/', $name, 1);
            // name is not a pathname, no such module found
            if (!$parent || !$name) return false;
            // name is a pathname, ask it's parent
            return self::$modules[$parent]->GetModule($name);
        }
    }
}